package backingbean;

import java.io.Serializable;
import java.util.Date;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

import controlador.CarritoEJBRemote;
import modelo.Carrito;
import modelo.DetalleCarro;
import modelo.Producto;

/**
 * ManagedBean que maneja las solicitudes del carrito de compras.
 * 
 * @author Boris Maclean.
 */
@ManagedBean
@SessionScoped
public class CarritoMB implements Serializable {

    /**
     * atributo correlativo implementado por la serializacion.
     */
    private static final long serialVersionUID = 1L;
    /**
     * El atributo bean asigna un mapeado ejb de tipo 'CarritoEJBRemote' para
     * solicitar metodos de esa interfaz.
     */
    @EJB(name = "CarritoEJBRemote", mappedName = "CarritoEJBRemote")
    private CarritoEJBRemote bean;
    /**
     * El atributo elCarro permite inicializar un objeto de tipo 'Carrito'
     * para ir definiendo los atributos y porsteriormente asignarlos a un
     * metodo del bean.
     */
    private Carrito elCarro = new Carrito();
    /**
     * El atributo userMB obtiene las propiedades del ManagedBean de session
     * UsuarioMB.
     */
    @ManagedProperty(value = "#{usuarioMB}")
    private UsuarioMB userMB;

    /**
     * El atributo nombreUser sirve solamente para asignar nombre completo de
     * la cuenta del usuario (nombre + apellido).
     */
    private String nombreUser;
    /**
     * El atributo usuarioLogeado es un boolean que verifica si el usuario
     * inicio sesion.
     */
    private boolean usuarioLogeado;
    /**
     * El atributo carroVacio es un boolean que verifica si el carro esta
     * vacio.
     */
    private boolean carroVacio;
    /**
     * El atributo totalPrecio sirve como contenedor del precio total que
     * tiene el carrito.
     */
    private int totalPrecio;
    /**
     * El atributo totalCantidad contiene el total de productos en el carrito.
     */
    private int totalCantidad;

    /**
     * El atributo msgComprar indica un mensaje por pantalla respecto a la a
     * si se realizo la compra o no.
     */
    private String msgComprar;

    /**
     * El atributo registrarse sirve para elaborar un boton de registro en
     * caso de que el usuario no se ha registrado.
     */
    private boolean registrarse;

    /**
     * El metodo init se ejecuta despues de crear este ManagedBean. Inicializa
     * el contenedor de productos del la interfaz ejb asociada al producto.
     * 
     */
    @PostConstruct
    public void init() {
        usuarioLogeado = comprobarUsuario();
        carroVacio = comprobarCarro();
        totalCantidad = cantidadProductos();
        totalPrecio = calcularTotal();
        msgComprar = "";
    }

    /**
     * El metodo comprobarUsuario permite verificar si existe un usuario en la
     * ManagedBean userMB.
     * 
     * @return true si existe, false en caso contrario.
     */
    private boolean comprobarUsuario() {
        if (!userMB.validarUserPass()) {
            return false;

        } else {
            this.nombreUser =
                    userMB.getUser().getNombre() + " "
                            + userMB.getUser().getApellido();
            return true;
        }
    }

    /**
     * El metodo modificarStock permite llamar a la sesion del managedbean
     * 'ProductoMB' y asignar nuevo stock utilizand los metodos de ese bean.
     * 
     */
    private void modificarStock() {
        ProductoMB productoMB =
                (ProductoMB) FacesContext.getCurrentInstance()
                        .getExternalContext().getSessionMap().get("productoMB");
        for (DetalleCarro d : this.elCarro.getDetalle()) {
            if (productoMB != null) {
                String id = d.getElProducto().getCodProducto();
                int nuevoStock =
                        productoMB.buscarProducto(id).getCantidad()
                                - d.getCantidad();
                productoMB.nuevoStock(id, nuevoStock);
            }
        }
    }

    /**
     * El metodo hayStock comprueba si el stock del producto es suficiente
     * para satisfacer la compra.
     * 
     * @param d
     *            : detalle de compra que se verificara la cantidad de
     *            productos son validor.
     * @return true si cumple los requisitos del stock, false en caso
     *         contrario.
     */
    public boolean hayStock(DetalleCarro d) {

        ProductoMB productoMB =
                (ProductoMB) FacesContext.getCurrentInstance()
                        .getExternalContext().getSessionMap().get("productoMB");
        String codigoP = d.getElProducto().getCodProducto();
        int stockActual = productoMB.buscarProducto(codigoP).getCantidad();
        if ((stockActual >= 1) && (d.getCantidad() < stockActual)) {
            return true;
        }
        return false;
    }

    /**
     * El metodo comprobarCarro permite verificar si el carro esta vacio o no.
     * 
     * @return true si esta vacio, false si no.
     */
    private boolean comprobarCarro() {
        return elCarro.getDetalle().isEmpty();
    }

    /**
     * El metodo calcularTotal permite obtener la sumatoria de los precios de
     * cada detalle en el carrito.
     * 
     * @return sumatoria del calculo.
     */
    private int calcularTotal() {
        int sumatoria = 0;
        for (DetalleCarro d : elCarro.getDetalle()) {
            sumatoria += (d.getCantidad() * d.getElProducto().getPrecio());
        }
        return sumatoria;
    }

    /**
     * El metodo cantidadProductos permite calcular el total de productos en
     * el carrito.
     * 
     * @return sumatoria de la cantidad de productos.
     */
    private int cantidadProductos() {
        int sumatoria = 0;
        for (DetalleCarro d : elCarro.getDetalle()) {
            sumatoria += d.getCantidad();
        }
        return sumatoria;
    }

    /**
     * El metodo agregarProducto permite agregar un nuevo producto al detalle
     * del carrito. Este metodo llama al metodo privado buscarProducto() e
     * init().
     * 
     * @param p
     *            : producto que se va a agregar.
     */
    public void agregarProducto(Producto p) {
        if (p.getCantidad() >= 1) {
            if (!buscarProducto(p.getCodProducto())) {
                elCarro.getDetalle().add(new DetalleCarro(elCarro, p, 1));
            }
        }
        init();
    }

    /**
     * El metodo eliminarProducto elimina el producto asociado al detalle
     * enviado por parametro.
     * 
     * 
     * @param d
     *            : detalle donde esta el producto.
     */
    public void eliminarProducto(DetalleCarro d) {
        elCarro.getDetalle().remove(d);
        init();
    }

    /**
     * El metodo sumarCantidad permite sumar '1' a la cantidad de producto
     * asociado al detalle enviado por parametro.
     * 
     * @param d
     *            : detalle donde esta el producto.
     */
    public void sumarCantidad(DetalleCarro d) {
        if (hayStock(d)) {
            d.setCantidad(d.getCantidad() + 1);

        }
        init();
    }

    /**
     * similar al metodo sumarCantidad(), solo que este resta '1'. Este metodo
     * llama al metodo init().
     * 
     * @param d
     *            : detalle donde esta el producto.
     */
    public void restarCantidad(DetalleCarro d) {
        if (d.getCantidad() > 1) {
            d.setCantidad(d.getCantidad() - 1);

        }
        init();
    }

    /**
     * El metodo buscarProducto permite buscar y/o sumar '1' en caso de que
     * encuentre el producto en el carrito asociado al id del parametro.
     * 
     * @param id
     *            : identificador unico del producto.
     * @return true si lo encuentra, false en caso contrario.
     */
    private boolean buscarProducto(String id) {
        for (DetalleCarro d : elCarro.getDetalle()) {
            if (d.getElProducto().getCodProducto().equals(id)) {
                if (hayStock(d)) {
                    d.setCantidad(d.getCantidad() + 1);
                }
                return true;
            }
        }
        return false;
    }

    /**
     * El metodo comprarCarrito permite agregar un nuevo carro mientras exista
     * una sesion de usuario ingresada.
     * 
     */
    public void comprarCarrito() {
        if (!comprobarUsuario()) {
            msgComprar = "NO HA INICIADO SESION, INGRESE AQUI.";
            registrarse = true;
        } else {
            elCarro.setUsuario(userMB.getUser());
            elCarro.setComprado(1);
            elCarro.setFechaCompra(new Date());
            modificarStock();
            bean.agregarCarrito(elCarro);
            elCarro = new Carrito();
            msgComprar = "COMPRA REALIZADA CON EXITO.";
            elCarro = new Carrito();
            registrarse = false;
        }
    }

    /**
     * El metodo mostrarCarrito redirecciona a la pagina "carrito.jsf" pero
     * llamando antes al metodo init().
     * 
     * @return pagina "carrito.jsf"
     */
    public String mostrarCarrito() {
        init();
        return "carrito";
    }

    // ////////////////////////////////////////////////////GETTER Y SETTER

    /**
     * Permite capturar el atributo elCarro.
     * 
     * @return el valor elCarro.
     */
    public Carrito getElCarro() {
        return elCarro;
    }

    /**
     * Permite asignar un valor al atributo elCarro.
     * 
     * @param elCarro
     *            es el nuevo valor para elCarro.
     */
    public void setElCarro(Carrito elCarro) {
        this.elCarro = elCarro;
    }

    /**
     * Permite capturar el atributo userMB.
     * 
     * @return el valor userMB.
     */
    public UsuarioMB getUserMB() {
        return userMB;
    }

    /**
     * Permite asignar un valor al atributo userMB.
     * 
     * @param userMB
     *            es el nuevo valor para userMB.
     */
    public void setUserMB(UsuarioMB userMB) {
        this.userMB = userMB;
    }

    /**
     * Permite capturar el atributo nombreUser.
     * 
     * @return el valor nombreUser.
     */
    public String getNombreUser() {
        return nombreUser;
    }

    /**
     * Permite asignar un valor al atributo nombreUser.
     * 
     * @param nombreUser
     *            es el nuevo valor para nombreUser.
     */
    public void setNombreUser(String nombreUser) {
        this.nombreUser = nombreUser;
    }

    /**
     * Permite capturar el atributo usuarioLogeado.
     * 
     * @return el valor usuarioLogeado.
     */
    public boolean isUsuarioLogeado() {
        return usuarioLogeado;
    }

    /**
     * Permite asignar un valor al atributo usuarioLogeado.
     * 
     * @param usuarioLogeado
     *            es el nuevo valor para usuarioLogeado.
     */
    public void setUsuarioLogeado(boolean usuarioLogeado) {
        this.usuarioLogeado = usuarioLogeado;
    }

    /**
     * Permite capturar el atributo carroVacio.
     * 
     * @return el valor carroVacio.
     */
    public boolean isCarroVacio() {
        return carroVacio;
    }

    /**
     * Permite asignar un valor al atributo carroVacio.
     * 
     * @param carroVacio
     *            es el nuevo valor para carroVacio.
     */
    public void setCarroVacio(boolean carroVacio) {
        this.carroVacio = carroVacio;
    }

    /**
     * Permite capturar el atributo totalPrecio.
     * 
     * @return el valor totalPrecio.
     */
    public int getTotalPrecio() {
        return totalPrecio;
    }

    /**
     * Permite asignar un valor al atributo totalPrecio.
     * 
     * @param totalPrecio
     *            es el nuevo valor para totalPrecio.
     */
    public void setTotalPrecio(int totalPrecio) {
        this.totalPrecio = totalPrecio;
    }

    /**
     * Permite capturar el atributo totalCantidad.
     * 
     * @return el valor totalCantidad.
     */
    public int getTotalCantidad() {
        return totalCantidad;
    }

    /**
     * Permite asignar un valor al atributo totalCantidad.
     * 
     * @param totalCantidad
     *            es el nuevo valor para totalCantidad.
     */
    public void setTotalCantidad(int totalCantidad) {
        this.totalCantidad = totalCantidad;
    }

    /**
     * Permite capturar el atributo msgComprar.
     * 
     * @return el valor msgComprar.
     */
    public String getMsgComprar() {
        return msgComprar;
    }

    /**
     * Permite asignar un valor al atributo msgComprar.
     * 
     * @param msgComprar
     *            es el nuevo valor para msgComprar.
     */
    public void setMsgComprar(String msgComprar) {
        this.msgComprar = msgComprar;
    }

    /**
     * Permite capturar el atributo registrarse.
     * 
     * @return el valor registrarse.
     */
    public boolean isRegistrarse() {
        return registrarse;
    }

    /**
     * Permite asignar un valor al atributo registrarse.
     * 
     * @param registrarse
     *            es el nuevo valor para registrarse.
     */
    public void setRegistrarse(boolean registrarse) {
        this.registrarse = registrarse;
    }

}
